﻿using System.Text;
namespace TH10_Dat_Tools
{
    /// <summary>
    /// 文件头
    /// </summary>
    public class TH_Head
    {
        /// <summary>
        /// 标志，TH_10对应为"THA1"
        /// </summary>
        public string Flag;
        /// <summary>
        /// 索引段原始大小
        /// </summary>
        public int originalSizeOfEntries;

        /// <summary>
        /// 经过Lzss压缩后的索引段大小
        /// </summary>
        public int compressedSizeofEntries;
        /// <summary>
        /// 文件数目
        /// </summary>
        public int numOfEntry;
    }

    /// <summary>
    /// 数据段索引节点
    /// </summary>
    public class TH_Entry
    {
        /// <summary>
        /// 文件名
        /// </summary>
        public string FileName;

        /// <summary>
        /// DAT文件偏移量
        /// </summary>
        public int offset;

        /// <summary>
        /// 压缩后数据大小
        /// </summary>
        public int compSize;

        /// <summary>
        /// 原始数据大小
        /// </summary>
        public int orgSize; 
    }

    /// <summary>
    /// 存储按位计算数据的结构
    /// </summary>
    public class BitData
    {
        /// <summary>
        /// 掩码
        /// </summary>
        public byte flag;

        /// <summary>
        /// 数据缓存，存储当前操作的那个字节
        /// </summary>
        public byte buf;

        /// <summary>
        /// 数据指针
        /// </summary>
        public int point;

        /// <summary>
        /// 数据
        /// </summary>
        public byte[] data;

        public BitData(byte[] data)
        {
            flag = 0x80;
            buf = 0;
            point = 0;
            this.data = data;
        }

        public BitData()
        {
            flag = 0x80;
            buf = 0;
            point = 0;
        }
    }

    public class Key
    {
        public byte k1;
        public byte k2;
        public int block;
        public int limit;
    }

    public class BitTools
    {

        public static void BitWriteEnd(BitData bd)
        {
            if (bd.flag == 0x80)
            {
                return;
            }
            else
            {
                bd.data[bd.point] = bd.buf;
                bd.point++;
            }
        }

        /// <summary>
        /// 读取bit数据
        /// </summary>
        /// <param name="bd">原数据结构</param>
        /// <param name="count">读取的bit数</param>
        /// <returns></returns>
        public static int BitRead(BitData bd, int count)
        {
            int ret = 0;
            for (int i = 1 << (count - 1); i > 0; i >>= 1)
            {
                //已经读取完上一个字节,则取下一个字节
                if (bd.flag == 0x80)
                {
                    if (bd.point < bd.data.Length)
                    {
                        bd.buf = bd.data[bd.point];
                        bd.point++;
                    }
                    //已经没有数据了
                    else
                    {
                        bd.buf = 0;
                    }
                }

                if ((bd.buf & bd.flag)!=0)
                {
                     ret |= i;
                }
                bd.flag >>= 1;
                if (bd.flag == 0)
                {
                        bd.flag = 0x80;
                }
            }
            return ret;
        }

        /// <summary>
        /// 比特写入
        /// </summary>
        /// <param name="bd">比特结构体</param>
        /// <param name="data">要写入的数据缓存</param>
        /// <param name="count">写入的比特数</param>
        public static void BitWrite(BitData bd, int data, int count)
        {
            for (int i = 1 << (count - 1); i > 0; i = i >> 1)
            {
                //空间写满
                if (bd.point >= bd.data.Length - 1)
                {
                    return;
                }

                if ((i & data)!=0)
                {
                    bd.buf = (byte)(bd.buf | bd.flag);
                }
                bd.flag = (byte)(bd.flag >> 1);

                //单字节写满
                if (bd.flag == 0x00)
                {
                    bd.flag = 0x80;
                    bd.data[bd.point] = bd.buf;
                    bd.buf = 0x00;
                    bd.point++;
                }

            }
        }
    }

    public class Tools
    {
        /// <summary>
        /// 合并byte数组
        /// </summary>
        /// <param name="a1"></param>
        /// <param name="a2"></param>
        /// <returns></returns>
        public static byte[] ByteAdd(byte[] a1,byte[]a2)
        {
            if (a1 == null && a2 == null)
                return null;

            if (a1 != null && a2 != null)
                if (a1.Length == 0 && a2.Length == 0)
                    return new byte[0];

            if (a1 == null || a1.Length == 0)
                return a2;

            if (a2 == null || a2.Length == 0)
                return a1;

            byte [] ret=new byte[a1.Length+a2.Length];

            a1.CopyTo(ret, 0);
            a2.CopyTo(ret, a1.Length);

            return ret;

        }

        /// <summary>
        /// 依据文件名计算密钥组编号
        /// </summary>
        /// <param name="str">文件名</param>
        /// <returns>编号</returns>
        public static int GetKeyID(string str)
        {
            int ret = 0;
            byte[] buff = Encoding.Default.GetBytes(str);
            for (int i = 0; i < str.Length; i++)
            {
                ret += buff[i];
            }
            return ret & 7;
        }

        /// <summary>
        /// 加密函数
        /// </summary>
        /// <param name="src">源数据</param>
        /// <param name="key">key1</param>
        /// <param name="step">key2</param>
        /// <param name="block">块大小</param>
        /// <param name="limit">最大处理量</param>
        /// <returns></returns>
        public static byte[] TH_Crypter(byte[] src, byte key, byte step, int block, int limit)
        {
            byte[] ret = new byte[src.Length];
            int src_point = 0;
            int tmp = limit;
            int ret_point = 0;
            int ret_base = 0;//当前处理块基地址
            int addup;//不处理的尾部数据
            int i;
            int size;


            addup = src.Length % block;
            if (addup >= block / 4)
                addup = 0;
            addup += src.Length % 2;
            size = src.Length - addup;

            while (size > 0 && limit > 0)
            {
                if (size < block)
                    block = size;

                ret_base = ret_point;
                ret_point = ret_point + block - 1;

                for (i = (block + 1) / 2; i > 0; --i, ++src_point)
                {
                    ret[src_point] = (byte)(src[ret_point] ^ key);
                    ret_point -= 2;
                    key += step;
                }

                ret_point = ret_base + block - 2;
                for (i = block / 2; i > 0; --i, src_point++)
                {
                    ret[src_point] = (byte)(src[ret_point] ^ key);
                    ret_point -= 2;
                    key += step;
                }
                size -= block;
                ret_point = ret_base + block;
                limit -= block;
            }
            size += addup;
            for (i = 0; i < size; i++)
            {
                ret[src_point] = src[src_point];
                src_point++;
            }
            return ret;
        }

        /// <summary>
        /// 解密函数
        /// </summary>
        /// <param name="src">源数据</param>
        /// <param name="key">key1</param>
        /// <param name="step">key2</param>
        /// <param name="block">数据块大小</param>
        /// <param name="limit">最大处理量</param>
        /// <returns>解码后的数据</returns>
        public static byte[] TH_UnCrypter(byte[] src, byte key, byte step, int block, int limit)
        {
            byte[] ret = new byte[src.Length];
            int src_point = 0;
            int tmp = limit;
            int ret_point = 0;
            int ret_base = 0;//当前处理块基地址
            int addup;//不处理的尾部数据
            int i;
            int size;


            addup = src.Length % block;
            if (addup >= block / 4)
                addup = 0;
            addup += src.Length % 2;
            size = src.Length - addup;

            while (size > 0 && limit > 0)
            {
                if (size < block)
                    block = size;

                ret_base = ret_point;
                ret_point = ret_point + block - 1;

                //单数
                for(i=(block+1)/2;i>0;--i,++src_point)
                {
                    ret[ret_point] = (byte)(src[src_point] ^ key);
                    ret_point -= 2;
                    key += step;
                }
                //双数
                ret_point = ret_base + block - 2;
                for (i = block / 2; i > 0; --i, src_point++)
                {
                    ret[ret_point] = (byte)(src[src_point] ^ key);
                    ret_point-=2;
                    key+=step;
                }
                size -= block;
                ret_point = ret_base + block;
                limit -= block;
            }
            size += addup;
            for (i = 0; i < size; i++)
            {
                ret[src_point] = src[src_point];
                src_point++;
            }
            return ret;
        }

        /// <summary>
        /// 数据解压缩函数
        /// </summary>
        /// <param name="src">源数据</param>
        /// <param name="orgSize">解压缩后的数据大小</param>
        /// <returns>解压缩后的数据</returns>
        public static byte[] TH_UnLzss(byte[] src,int orgSize)
        {
            byte[] ret = new byte[orgSize];//解压缩后的数据
            int ret_point = 0;//解压缩数据指针
            BitData bd = new BitData(src);
            int flag;//压缩标记
            int buf;//数据缓存
            int dict_size=0x2000;//字典大小
            byte[] dict = new byte[dict_size];//字典数据
            int dict_point = 1;//字典指针
            int data_point = 0;//压缩数据在字典中的位置
            int data_len = 0;//压缩数据长度

            while (orgSize > 0)
            {
                flag = BitTools.BitRead(bd, 1);//读取压缩标记
                
                //非压缩数据
                if (flag != 0)
                {
                    buf = BitTools.BitRead(bd, 8);
                    ret[ret_point] = (byte)buf;
                    ret_point++;
                    dict[dict_point] = (byte)buf;
                    dict_point = (dict_point + 1) % dict_size;
                    --orgSize;
                }
                //压缩数据
                else
                {
                    data_point = BitTools.BitRead(bd, 13);//读取压缩数据在字典中的位置
                    if (data_point == 0)
                        break;
                    data_len = BitTools.BitRead(bd, 4) + 2;//读取压缩片段长度

                    //解压缩
                    for (int i = 0; i <= data_len && orgSize > 0; ++i, --orgSize)
                    {
                        buf = dict[(data_point + i) % dict_size];
                        ret[ret_point] = (byte)buf;
                        ret_point++;
                        dict[dict_point] = (byte)buf;
                        dict_point = (dict_point + 1) % dict_size;
                    }
                }
            }
            return ret;
        }

        /// <summary>
        /// 数据压缩
        /// </summary>
        /// <param name="src">原始数据</param>
        /// <returns>压缩后的数据</returns>
        public static byte[] TH_Lzss(byte[] src)
        {
            int dict_size = 0x2000;
            byte[] dict = new byte[dict_size];
            byte[] ret=null;
            BitData bd = new BitData();
            bd.data = new byte[src.Length*2];
            for (int i = 0; i < src.Length; i++)
            {
                BitTools.BitWrite(bd, 1, 1);
                BitTools.BitWrite(bd, src[i], 8);
            }

            BitTools.BitWriteEnd(bd);
            ret = new byte[bd.point];
            for (int x = 0; x < ret.Length; x++)
                ret[x] = bd.data[x];

            return ret;
        }
    }
}